package Offer16;

import java.util.*;

/**
 * 最接近的三数之和
 * 给定一个包括n 个整数的数组nums和 一个目标值target。找出nums中的三个整数.
 * 使得它们的和与target最接近。返回这三个数的和。假定每组输入只存在唯一答案。
 * @author 23737
 * @time 2021.11.03
 */
public class TestTwice {
    public static void main(String[] args) {
//        System.out.println(new TestTwice().threeSumClosest(new int[]{-1, 2, 1, -4}, 1));
        System.out.println(new TestTwice().threeSumClosest2(new int[]{0,1,2}, 1));
    }

    /**
     * 这个想法是正确的，答案也是正确的。力扣竟然不认NavigableMap，太蠢了。
     * @param nums
     * @param target
     * @return
     */
    public int threeSumClosest(int[] nums, int target) {
        int sum = 0;
        Arrays.sort(nums);
        NavigableMap<Integer,Integer>  map = new TreeMap<>();
//        System.out.println(nums[0]+nums[1]+nums[2]);
        int min = Math.abs(target - (nums[0]+nums[1]+nums[2]));
//        System.out.println(min);
        for (int i = 0; i < nums.length; i++) {
            for (int j = i+1; j < nums.length; j++) {
                for (int k = j+1; k < nums.length; k++) {
                    sum = nums[i]+nums[j]+nums[k];
                    int compare = Math.abs(target - sum);
                    if (compare < min){
                        min = compare;
                        map.put(min,sum);
                    }
                }
            }
        }
//        for (Map.Entry<Integer, Integer> integerEntry : map.entrySet()) {
//            System.out.println("k-->"+integerEntry.getKey() + "  ||  v-->"+integerEntry.getValue());
//        }
        Map.Entry<Integer, Integer> integerIntegerEntry = map.firstEntry();
        Integer value = integerIntegerEntry.getValue();
//        System.out.println(value);
        return value;
    }

    /**
     *为了满足力扣而写的第二道算法
     * @param nums
     * @param target
     * @return
     */
    public int threeSumClosest2(int[] nums, int target) {
        int sum = 0;
        //首先将数组排序
        Arrays.sort(nums);
        //使用map来进行数据存储，key放最小值，value放最小值对应的三数的和
        Map<Integer,Integer>  map = new TreeMap<>();
        //初始化最小值，设置最小值为 （目标值-三数之和）的绝对值
        int min = Math.abs(target - (nums[0]+nums[1]+nums[2]));
//        System.out.println(min);
        //这里要注意，必须先将初始化的最小值和三数之和存到map里边。因为如果数组只有三个元素，map就无法存储
        map.put(min,nums[0]+nums[1]+nums[2]);
        for (int i = 0; i < nums.length; i++) {
            for (int j = i+1; j < nums.length; j++) {
                for (int k = j+1; k < nums.length; k++) {
                    sum = nums[i]+nums[j]+nums[k];
//                    System.out.println(sum);
                    //设置一个比较值，如果比较值小于最小值，就将min设置为compare，并存入map中，确保map中始终存入的是最小值
                    int compare = Math.abs(target - sum);
//                    System.out.println(compare);
                    if (compare < min){
                        min = compare;
                        map.put(min,sum);
                    }
                }
            }
        }
        int result  = 0;
        int i = 1;
//        map.forEach((k,v)->{
//            System.out.println("k-->"+k+" || v-->"+v);
//        });
        //因为是treemap，所以直接获得第一个key对应的value就好了
        for (Integer integer : map.keySet()) {
            result = map.get(integer);
            if (i == 1) break;
        }
//        System.out.println(result);
        return result;
    }
}

/**
 * 官方题解
 */
class SolutionOffer {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int n = nums.length;
        int best = 10000000;

        // 枚举 a
        for (int i = 0; i < n; ++i) {
            // 保证和上一次枚举的元素不相等
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            // 使用双指针枚举 b 和 c
            int j = i + 1, k = n - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                // 如果和为 target 直接返回答案
                if (sum == target) {
                    return target;
                }
                // 根据差值的绝对值来更新答案
                if (Math.abs(sum - target) < Math.abs(best - target)) {
                    best = sum;
                }
                if (sum > target) {
                    // 如果和大于 target，移动 c 对应的指针
                    int k0 = k - 1;
                    // 移动到下一个不相等的元素
                    while (j < k0 && nums[k0] == nums[k]) {
                        --k0;
                    }
                    k = k0;
                } else {
                    // 如果和小于 target，移动 b 对应的指针
                    int j0 = j + 1;
                    // 移动到下一个不相等的元素
                    while (j0 < k && nums[j0] == nums[j]) {
                        ++j0;
                    }
                    j = j0;
                }
            }
        }
        return best;
    }
}


